using System;
using System.Collections.Generic;
using System.IO;

namespace Cirrostrata.Abstractions.IO
{
    public class DefaultFileSystem : IFileSystem
    {
        public virtual void Copy(string sourcePath, string destPath)
        {
            File.Copy(sourcePath, destPath, true);
        }

        public virtual void SaveText(string path, TextReader source, bool preCreateDir)
        {
            if (preCreateDir)
                PreCreateDirectory(path);

            using (var writer = new StreamWriter(path))
            {
                const int BUFFER_SIZE = 131072;
                var buf = new char[BUFFER_SIZE];
                int read;

                do
                {
                    read = source.Read(buf, 0, BUFFER_SIZE);
                        
                    if (read > 0)
                        writer.Write(buf, 0, read);

                } while (read > 0);
            }
        }

        public virtual TextReader LoadText(string path)
        {
            return new StreamReader(path);
        }

        public virtual void SaveBinary(string path, BinaryReader source, bool preCreateDir)
        {
            if (preCreateDir)
                PreCreateDirectory(path);

            using (var destination = new FileStream(path, FileMode.Create))
            {
                const int BUFFER_SIZE = 262144;
                var buf = new byte[BUFFER_SIZE];
                int read;

                do
                {
                    read = source.Read(buf, 0, BUFFER_SIZE);

                    if (read > 0)
                        destination.Write(buf, 0, read);

                } while (read > 0);
            }
        }

        public virtual BinaryReader LoadBinary(string path)
        {
            return new BinaryReader(new FileStream(path, FileMode.Open));
        }

        public virtual void SaveStream(string path, Stream source, bool preCreateDir)
        {
            SaveBinary(path, new BinaryReader(source), preCreateDir);
        }

        public virtual Stream LoadStream(string path)
        {
            return new FileStream(path, FileMode.Open, FileAccess.Read);
        }

        public virtual bool FileExists(string path)
        {
            return File.Exists(path);
        }

        public virtual IFileSystemMetaData GetMetaData(string path)
        {
            if (File.Exists(path))
                return new FileSystemMetaData(new FileInfo(path));

            return new FileSystemMetaData(new DirectoryInfo(path));
        }

        public virtual IEnumerable<IFileSystemMetaData> GetFiles(string path, string pattern, bool recurse)
        {
            var di = new DirectoryInfo(path);

            if (di.Exists)
            {
                foreach (
                    FileInfo fi in
                        di.GetFiles(pattern, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
                    yield return new FileSystemMetaData(fi);
            }
        }

        public virtual IEnumerable<IFileSystemMetaData> GetDirectories(string path, string pattern, bool recurse)
        {
            var di = new DirectoryInfo(path);

            if (di.Exists)
            {
                foreach (
                    DirectoryInfo sdi in
                        di.GetDirectories(pattern, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                    )
                    yield return new FileSystemMetaData(sdi);
            }
        }

        public virtual void Delete(string path)
        {
            if (File.Exists(path))
                File.Delete(path);
        }

        public virtual void CreateDirectory(string path)
        {
            Directory.CreateDirectory(path);
        }

        public virtual void DeleteDirectory(string path)
        {
            if (Directory.Exists(path))
                Directory.Delete(path, true);
        }

        private static void PreCreateDirectory(string path)
        {
            string dir = Path.GetDirectoryName(path);

            if (dir == null)
                throw new ArgumentException(String.Format("Could not extract directory from path: \"{0}\"", path));

            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
        }
    }
}